package czk.concrent.reentrantLock.wheellock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 问题：
 * 如果遇到有一个线程一直霸占或者长时间霸占锁资源的情况，就会导致这个轮询锁进入死循环的状态，
 * 它会尝试一直获取锁资源，这样就会造成新的问题，带来不必要的性能开销
 * <p>
 * 解决：添加最大次数限制：如果经过了 n 次尝试获取锁之后，还未获取到锁，则认为获取锁失败，执行失败策略之后终止轮询（失败策略可以是记录日志或其他操作）；
 * 添加最大时长限制：如果经过了 n 秒尝试获取锁之后，还未获取到锁，则认为获取锁失败，执行失败策略之后终止轮询。
 *
 * @Author:ChenZhangKun
 * @Date: 2021/8/31 10:26
 */
public class Demo3 {
    public static void main(String[] args) {
        Lock lock1 = new ReentrantLock();
        Lock lock2 = new ReentrantLock();
        new Thread(() -> {
            // 调用轮询锁
            pollingLock(lock1, lock2);
        }).start();

        new Thread(() -> {
            try {
                lock2.lock();
                System.out.println("线程二获取到锁：B");
                // 获取锁二
                Thread.sleep(1000);
                System.out.println("线程二等待获取锁：A");
                try {
                    lock1.lock();
                    System.out.println("线程二获取到了锁：A");
                } finally {
                    lock1.unlock();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 这里线程B不释放锁，导致线程A空转，超过5次则退出循环
                // lock2.unlock();
            }
        }).start();
    }

    private static void pollingLock(Lock lock1, Lock lock2) {
        // 添加计数器
        int count = 0;
        while (true) {
            if (lock1.tryLock()) {
                System.out.println("线程一拿到锁：A");
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("线程一等待获取锁：B");
                    if (lock2.tryLock()) {
                        try {
                            // 获取到锁B
                            System.out.println("线程一获取到锁：B");
                        } finally {
                            System.out.println("线程一释放锁：B");
                            // 释放锁
                            lock2.unlock();
                            break;
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放锁A
                    lock1.unlock();
                    System.out.println("线程一释放锁A");
                }
                // 计算释放次数
                // 5次还没有释放锁则退出循环
                if (++count > 5) {
                    //
                    System.out.println("5次没有释放锁，退出循环");
                    return;
                }
            }
            // 等待一秒再继续尝试获取锁
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
